//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <mantle.h>
#include <clsinfo.h>
#include <stub.h>
#include <kmarshal.h>
#include <kprxstub.h>
#include <idmanage.h>
#include "rot.h"

static SLinkNode  s_kerImportObjHash[CFG_IMPORTOBJECT_HS];
static SLinkNode  s_kerExportObjHash[CFG_EXPORTOBJECT_HS];
#ifdef DEBUG_KMUTEX
static KMutex s_importObjLock(__FILE__, __LINE__);
static KMutex s_exportObjLock (__FILE__, __LINE__);
KMutex g_marshalLock(__FILE__, __LINE__);
#else
static KMutex s_importObjLock;
static KMutex s_exportObjLock;
KMutex g_marshalLock;
#endif //DEBUG_KMUTEX

// global marshal vtbl
//
UInt32 g_marshalVtbl[MSH_MAX_METHODS];

DECL_CODEINIT void CDECL InitializeROT()
{
    int i;

    for (i = 0; i < CFG_IMPORTOBJECT_HS; i++) {
        s_kerImportObjHash[i].Initialize();
    }

    for (i = 0; i < CFG_EXPORTOBJECT_HS; i++) {
        s_kerExportObjHash[i].Initialize();
    }

    // Hook all interfaces' IInterface to CObjectProxy except Aggregate and
    // Fill v-table with proxy entry.
    // The proxy entrys's array begin from PROXY_ENTRY_BASE (0x7ffef000).
    //
    g_marshalVtbl[0] = (UInt32)&CInterfaceProxy::S_Probe;
    g_marshalVtbl[1] = (UInt32)&CInterfaceProxy::S_AddRef;
    g_marshalVtbl[2] = (UInt32)&CInterfaceProxy::S_Release;
    for (i = 3; i < MSH_MAX_METHODS; i++) {
        g_marshalVtbl[i] = PROXY_ENTRY_BASE + ((i - 3) << PROXY_ENTRY_SHIFT);
    }
}

ECode RegisterKernExportObject(
    /* [in] */ oid_t oid,
    /* [in] */ IInterface *pObject,
    /* [in] */ IStub *pIStub)
{
    ExportObject * pExportObject;

    pObject = pObject->Probe(EIID_IInterface);
    if (!pObject) {
        return E_NO_INTERFACE;
    }
    pObject->AddRef();

    pExportObject = new ExportObject;
    if (NULL == pExportObject) {
        pObject->Release();
        return E_OUT_OF_MEMORY;
    }

    pExportObject->m_oid = oid;
    pExportObject->m_pObject = pObject;
    pExportObject->m_pIStub = pIStub;
    s_exportObjLock.Lock();
    s_kerExportObjHash[oid & HASH2MASK(CFG_EXPORTOBJECT_HS)].\
        InsertFirst(pExportObject);
    s_exportObjLock.Unlock();

    return NOERROR;
}

ECode FindKernExportObject(
    /* [in] */ IInterface *pObject,
    /* [out] */ ExportObject *pExport)
{
    SLinkNode * pNode;

    pObject = pObject->Probe(EIID_IInterface);
    if (!pObject) {
        return E_NO_INTERFACE;
    }

    s_exportObjLock.Lock();
    for (int n = 0; n < CFG_EXPORTOBJECT_HS; n++) {
        pNode = s_kerExportObjHash[n].Next();
        while (pNode != &s_kerExportObjHash[n]) {
            if (((ExportObject *)pNode)->m_pObject == pObject) {
                memcpy(pExport, pNode, sizeof(ExportObject));
                pExport->m_pIStub->AddRef();
                s_exportObjLock.Unlock();
                return NOERROR;
            }
            pNode = pNode->Next();
        }
    }
    s_exportObjLock.Unlock();
    return E_NO_EXPORT_SERVER;
}

ECode FindKernExportObject(
    /* [in] */ oid_t oid,
    /* [out] */ ExportObject *pExport)
{
    SLinkNode * pHead;
    SLinkNode * pCur;

    s_exportObjLock.Lock();
    pHead = &s_kerExportObjHash[oid & HASH2MASK(CFG_EXPORTOBJECT_HS)];
    pCur = pHead->Next();

    while (pCur != pHead) {
        if (((ExportObject *)pCur)->m_oid == oid) {
            memcpy(pExport, pCur, sizeof(ExportObject));
            pExport->m_pIStub->AddRef();
            s_exportObjLock.Unlock();
            return NOERROR;
        }
        pCur = pCur->Next();
    }

    s_exportObjLock.Unlock();
    return E_NO_EXPORT_SERVER;
}

ECode UnregisterKernExportObject(
    /* [in] */ oid_t oid)
{
    SLinkNode * pPrev;
    SLinkNode * pCur;
    SLinkNode * pHead;

    s_exportObjLock.Lock();
    pHead = &s_kerExportObjHash[oid & HASH2MASK(CFG_EXPORTOBJECT_HS)];
    pPrev = pHead;
    pCur = pHead->Next();

    while (pCur != pHead) {
        ExportObject * p = (ExportObject *)pCur;
        if (p->m_oid == oid) {
            if ((Int32)(((CObjectStub*)(p->m_pIStub))->m_cRefs) != 0) {
                s_exportObjLock.Unlock();
                return S_FALSE;
            }
            pCur->Detach(pPrev);
            s_exportObjLock.Unlock();
            p->m_pObject->Release();
            delete(p);

            return NOERROR;
        }
        pPrev = pCur;
        pCur = pCur->Next();
    }

    s_exportObjLock.Unlock();
    return E_NO_EXPORT_SERVER;
}

ECode RegisterKernImportObject(
    /* [in] */ oid_t oid,
    /* [in] */ IProxy *pIProxy)
{
    ImportObject * pImportObject;

    pImportObject = new ImportObject;
    if (NULL == pImportObject) {
        return E_OUT_OF_MEMORY;
    }
    pImportObject->m_oid = oid;
    pImportObject->m_pIProxy = pIProxy;
    s_importObjLock.Lock();
    s_kerImportObjHash[oid & HASH2MASK(CFG_IMPORTOBJECT_HS)].\
        InsertFirst(pImportObject);
    s_importObjLock.Unlock();

    return NOERROR;
}

ECode FindKernImportObject(
    /* [in] */ oid_t oid,
    /* [out] */ ImportObject *pImport)
{
    SLinkNode * pHead;
    SLinkNode * pCur;

    s_importObjLock.Lock();
    pHead = &s_kerImportObjHash[oid & HASH2MASK(CFG_IMPORTOBJECT_HS)];
    pCur = pHead->Next();

    while (pCur != pHead) {
        if (((ImportObject *)pCur)->m_oid == oid) {
            memcpy(pImport, pCur, sizeof(ImportObject));
            pImport->m_pIProxy->AddRef();
            s_importObjLock.Unlock();
            return NOERROR;
        }
        pCur = pCur->Next();
    }

    s_importObjLock.Unlock();
    return E_NO_IMPORT_SERVER;
}

ECode UnregisterKernImportObject(
    /* [in] */ oid_t oid)
{
    SLinkNode * pPrev;
    SLinkNode * pCur;
    SLinkNode * pHead;

    s_importObjLock.Lock();
    pHead = &s_kerImportObjHash[oid & HASH2MASK(CFG_IMPORTOBJECT_HS)];
    pPrev = pHead;
    pCur = pHead->Next();

    while (pCur != pHead) {
        ImportObject * p = (ImportObject *)pCur;
        if (p->m_oid == oid) {
            if ((Int32)(((CObjectProxy*)(p->m_pIProxy))->m_cRefs) != 0) {
                s_importObjLock.Unlock();
                return S_FALSE;
            }
            pCur->Detach(pPrev);
            s_importObjLock.Unlock();
            delete(p);
            return NOERROR;
        }
        pPrev = pCur;
        pCur = pCur->Next();
    }

    s_importObjLock.Unlock();
    return E_NO_IMPORT_SERVER;
}
